﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Farigola.Engine.SystemData.Entity.inst;
using System.Xml;
using Farigola.Engine.Factories;
using Farigola.Engine.SystemData.Entity.Type;
using Farigola.Engine.SystemData.DataValue;
using Farigola.Engine.SystemData.Entity;
using Farigola.Engine.PersistXml.TestUnit.DataBuild;

namespace Farigola.Engine.PersistXml
{
    /// <summary>
    /// Transform the project content to a xml document.
    /// Used for save.
    /// </summary>
    public class TransformProjectToXml
    {
        //=====================================================================
        #region Data members.

        /// <summary>
        /// Need factory to build objects, to check data,...
        /// </summary>
        private Factory _factory;

        #endregion

        //=====================================================================
        #region Constructor.

        //---------------------------------------------------------------------
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="factory"></param>
        public TransformProjectToXml(Factory factory)
        {
            _factory = factory;
        }

        #endregion

        //=====================================================================
        #region Private data value put to xml sub methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// put to xml  the dataValue string.
        /// 
        /// <dv type="string"> ... </dv>
        /// 
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <param name="dataValueString"></param>
        /// <returns></returns>
        private XmlElement PutDataValueStringToXml(XmlDocument xmlDoc, DataValueString dataValueString)
        {
            // crate the main xml elt: <dv type="string">...
            XmlElement elt = XmlUtils.CreateElt(xmlDoc, ProjectRepositXmlDef.XmlEltDataValue);
            XmlUtils.CreateAttribAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltDataValueAttribType, ProjectRepositXmlDef.XmlEltDataValueAttribTypeString);

            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltDataValueIsNull, dataValueString.IsNull.ToString());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltDataValueIsNullable, dataValueString.IsNullable.ToString());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltDataValueValue, "\"" + dataValueString.GetValue() + "\"");

            return elt;
        }

        #endregion

        //=====================================================================
        #region Private entity instance put to xml sub methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// is it an ent inst basic type?
        /// </summary>
        /// <param name="?"></param>
        /// <param name="entInst"></param>
        /// <returns></returns>
        private XmlElement PutEntInstStringToXml(XmlDocument xmlDoc, EntInstString entInst)
        {
            // crate the main xml elt: <entity type="EntInstString">...
            XmlElement elt = XmlUtils.CreateElt(xmlDoc, ProjectRepositXmlDef.XmlEltEntity);
            XmlUtils.CreateAttribAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltEntAttribType, ProjectRepositXmlDef.XmlEltEntInstString);

            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltRev, ProjectRepositXmlDef.LastRevNumEntInstString);
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltId, entInst.GetId().ToString());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltName, entInst.GetName());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltSystOrUser, entInst.GetTypeSystOrUser().ToString());

            // add the dataValue of the string
            XmlElement xmlEltDataValue= PutDataValueStringToXml(xmlDoc, entInst.GetDataValueString());

            if(xmlEltDataValue!=null)
                elt.AppendChild(xmlEltDataValue);

            return elt;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Convert/transform the struct list (and items) to xml.
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <param name="entInst"></param>
        /// <returns></returns>
        public XmlElement PutEntInstStructListToXml(XmlDocument xmlDoc, EntInstStructList entInst)
        {
            // create the main xml elt
            XmlElement elt = XmlUtils.CreateElt(xmlDoc, ProjectRepositXmlDef.XmlEltEntity);
            XmlUtils.CreateAttribAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltEntAttribType, ProjectRepositXmlDef.XmlEltEntInstStructList);

            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltRev, ProjectRepositXmlDef.LastRevNumEntInstString);
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltId, entInst.GetId().ToString());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltName, entInst.GetName());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltSystOrUser, entInst.GetTypeSystOrUser().ToString());

            // add the type of items
            // todo:

            // add items
            // todo:

            return elt;
        }

        //---------------------------------------------------------------------
        public XmlElement PutEntInstClassToXml(XmlDocument xmlDoc, EntInstClass entInst)
        {
            // create the main xml elt
            XmlElement elt = XmlUtils.CreateElt(xmlDoc, ProjectRepositXmlDef.XmlEltEntity);
            XmlUtils.CreateAttribAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltEntAttribType, ProjectRepositXmlDef.XmlEltEntInstClass);

            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltRev, ProjectRepositXmlDef.LastRevNumEntInstString);
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltId, entInst.GetId().ToString());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltName, entInst.GetName());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltSystOrUser, entInst.GetTypeSystOrUser().ToString());

            // add the type of items
            // todo:

            // add items
            // todo:

            return elt;
        }

        //---------------------------------------------------------------------
        public XmlElement PutEntInstClassAnonymToXml(XmlDocument xmlDoc, EntInstClassAnonym entInst)
        {
            // create the main xml elt
            XmlElement elt = XmlUtils.CreateElt(xmlDoc, ProjectRepositXmlDef.XmlEltEntity);
            XmlUtils.CreateAttribAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltEntAttribType, ProjectRepositXmlDef.XmlEltEntInstClassAnonym);

            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltRev, ProjectRepositXmlDef.LastRevNumEntInstString);
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltId, entInst.GetId().ToString());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltName, entInst.GetName());
            XmlUtils.CreateEltAdd(xmlDoc, elt, ProjectRepositXmlDef.XmlEltSystOrUser, entInst.GetTypeSystOrUser().ToString());

            // add the items
            // todo:

            return elt;
        }


        //---------------------------------------------------------------------
        /// <summary>
        /// is it an ent inst basic type?
        /// </summary>
        /// <param name="?"></param>
        /// <param name="entInst"></param>
        /// <returns></returns>
        private XmlElement PutEntInstBasicBaseToXml(XmlDocument xmlDoc, EntInstBasicBase entInst)
        {
            // is it a ent inst string?
            if (entInst is EntInstString)
            {
                return PutEntInstStringToXml(xmlDoc, entInst as EntInstString);
            }

            // is it a inst int?
            // todo:

            // is it a inst double?
            // todo:

            return null;
        }

        //---------------------------------------------------------------------
        private XmlElement PutEntInstStructBaseToXml(XmlDocument xmlDoc, EntInstStructureBase entInst)
        {
            // is it a ent inst struct list?
            if (entInst is EntInstStructList)
            {
                return PutEntInstStructListToXml(xmlDoc, entInst as EntInstStructList);
            }

            // is it a struct table?
            // todo:

            // is it a struct queue, stack...?
            // todo:

            return null;
        }

        //---------------------------------------------------------------------
        private XmlElement PutEntInstClassBaseToXml(XmlDocument xmlDoc, EntInstClassBase entInst)
        {
            // is it a ent inst class std?
            if (entInst is EntInstClass)
            {
                return PutEntInstClassToXml(xmlDoc, entInst as EntInstClass);
            }

            // is it a ent inst class anonym?
            if (entInst is EntInstClassAnonym)
            {
                return PutEntInstClassAnonymToXml(xmlDoc, entInst as EntInstClassAnonym);
            }

            return null;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// build xml of the entity instance base.
        /// 
        /// structure:
        /// <entInst type="folder">
        ///   ...
        /// </entInst>
        /// 
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <param name="entInst"></param>
        /// <returns></returns>
        private XmlElement PutEntInstBaseToXml(XmlDocument xmlDoc, EntInstBase entInst)
        {
            // is it an ent inst folder?
            // todo: deja traité ailleurs, a moins de faire la partie générale sauf les childs 

            // is it an ent inst basic type?
            if (entInst is EntInstBasicBase)
            {
                return PutEntInstBasicBaseToXml(xmlDoc, entInst as EntInstBasicBase);
            }

            // is it an ent inst struct list?
            if (entInst is EntInstStructureBase)
            {
                return PutEntInstStructBaseToXml(xmlDoc, entInst as EntInstStructureBase);
            }

            // is it an ent inst class (std or anonym)?
            if (entInst is EntInstClassBase)
            {
                return PutEntInstClassBaseToXml(xmlDoc, entInst as EntInstClassBase);
            }

            // todo:

            return null;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// put the content of the inst folder to xml.
        /// 
        /// <Entity type="folder">...</Entity>
        /// 
        /// </summary>
        /// <param name="parentFoder"></param>
        /// <param name="xmlDocTypes"></param>
        /// <param name="xmlDocEntityTree"></param>
        /// <returns></returns>
        private XmlElement PutInstFolderContentToXml(EntInstFolder parentFoder, XmlDocument xmlDocTypes, XmlDocument xmlDocEntityTree)
        {
            XmlElement xmlElt;

            if (parentFoder == null)
                return null;

            // crate the main xml elt: <entity type="EntInstFolder">...
            XmlElement folderElt = XmlUtils.CreateElt(xmlDocEntityTree, ProjectRepositXmlDef.XmlEltEntity);
            XmlUtils.CreateAttribAdd(xmlDocEntityTree, folderElt, ProjectRepositXmlDef.XmlEltEntAttribType, ProjectRepositXmlDef.XmlEltEntInstFolder);

            XmlUtils.CreateEltAdd(xmlDocEntityTree, folderElt, ProjectRepositXmlDef.XmlEltRev, ProjectRepositXmlDef.LastRevNumEntInstFolder);
            XmlUtils.CreateEltAdd(xmlDocEntityTree, folderElt, ProjectRepositXmlDef.XmlEltId, parentFoder.GetId().ToString());
            XmlUtils.CreateEltAdd(xmlDocEntityTree, folderElt, ProjectRepositXmlDef.XmlEltName, parentFoder.GetName());
            XmlUtils.CreateEltAdd(xmlDocEntityTree, folderElt, ProjectRepositXmlDef.XmlEltSystOrUser, parentFoder.GetTypeSystOrUser().ToString());
            // todo: add others membres/fields of the inst folder 

            // create elt childs of the folder
            XmlElement xmlEltChilds= XmlUtils.CreateEltAdd(xmlDocEntityTree, folderElt, ProjectRepositXmlDef.XmlEltEntInstFolderChilds);

            // scan the entityTree, if find a type save it (with the position folder and order in childs)
            foreach (EntityBase currEntInst in parentFoder.GetListChild())
            {
                // if its a folder, now scan childs
                if (currEntInst is EntInstFolder)
                {
                    // its a folder, process the folder 
                    XmlElement xmlEltChildContent= PutInstFolderContentToXml(currEntInst as EntInstFolder, xmlDocTypes, xmlDocEntityTree);

                    // add xmlEltChildContent to xmlEltChilds 
                    if(xmlEltChildContent != null)
                       xmlEltChilds.AppendChild(xmlEltChildContent);

                    // go to the next child ( to avoid add it again in EntInstBase part, because the fodler is a ent inst base)
                    continue;
                }

                // is it a type class?
                if (currEntInst is EntTypeClassBase)
                {
                    // todo:
                }

                // is it an inst?  (and not a folder!)                
                if (currEntInst is EntInstBase)
                {
                    // build xml of the entity instance base
                    xmlElt = PutEntInstBaseToXml(xmlDocEntityTree, currEntInst as EntInstBase);

                    // put the xml content to the folder xml content
                    if(xmlElt != null)
                        xmlEltChilds.AppendChild(xmlElt);
                    continue;
                }

            }

            return folderElt;
        }

        #endregion


        //=====================================================================
        #region Public Save (ToXml) Main methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// save generals parameters of the project,
        /// put all data in a xml document.
        /// </summary>
        /// <param name="project"></param>
        /// <returns></returns>
        public XmlDocument PutProjectParamsToXml(Project prj)
        {
            XmlDocument xmlDoc = new XmlDocument();

            // let's add the XML declaration section
            XmlUtils.CreateEltXmlDeclaration(xmlDoc);

            XmlElement paramsElt = XmlUtils.CreateEltAdd(xmlDoc, ProjectRepositXmlDef.XmlEltRootParams);

            XmlUtils.CreateEltAdd(xmlDoc, paramsElt, ProjectRepositXmlDef.XmlEltRev, ProjectRepositXmlDef.LastRevNumProject);
            XmlUtils.CreateEltAdd(xmlDoc, paramsElt, ProjectRepositXmlDef.XmlEltDateCre, prj.GetDateCreationStr(ProjectRepositXmlDef.dateTimeFormat));

            XmlElement idElt = XmlUtils.CreateEltAdd(xmlDoc, paramsElt, ProjectRepositXmlDef.XmlEltId, prj.GetId().ToString());

            XmlElement nameElt = XmlUtils.CreateEltAdd(xmlDoc, paramsElt, ProjectRepositXmlDef.XmlEltName, prj.GetName());

            return xmlDoc;
        }


        //---------------------------------------------------------------------
        /// <summary>
        /// put project content to two xml doc:
        /// types (build by the user,
        /// entity tree , all entities.
        /// </summary>
        /// <param name="prj"></param>
        /// <returns></returns>
        public bool PutProjectContentToXml(Project prj, out XmlDocument xmlDocTypes, out XmlDocument xmlDocEntityTree)
        {
            XmlElement xmlElt;

            xmlDocTypes = new XmlDocument();
            xmlDocEntityTree = new XmlDocument();

            // start create the types xmlDoc
            XmlUtils.CreateEltXmlDeclaration(xmlDocTypes);
            XmlElement typesElt = XmlUtils.CreateEltAdd(xmlDocTypes, ProjectRepositXmlDef.XmlEltRootTypes);

            // start create the entityTree xmlDoc
            XmlUtils.CreateEltXmlDeclaration(xmlDocEntityTree);
            XmlElement entityTreeElt = XmlUtils.CreateEltAdd(xmlDocEntityTree, ProjectRepositXmlDef.XmlEltRootEntityTree);

            // start scan from the root folder of the project
            EntInstFolder rootFoder = prj.GetRootFolder();

            // transform the folder and childs to xml
            xmlElt = PutInstFolderContentToXml(rootFoder, xmlDocTypes, xmlDocEntityTree);

            // add the xml content to the xml document
            entityTreeElt.AppendChild(xmlElt);
            return true;
        }

        #endregion

    }
}
